﻿/* SCE CONFIDENTIAL
$PSLibId$
* Copyright (C) 2011 Sony Computer Entertainment Inc.
* All Rights Reserved.
*/

#define USE_PSP2_DUMP_RENDER 0
#if USE_PSP2_DUMP_RENDER
#include "config.h"
#include "psp2-test.h"
#include "AccessibilityController.h"
#include "EditingCallbacks.h"
#include "EventSender.h"
#include "GCController.h"
#include "LayoutTestController.h"
#include "PixelDumpSupport.h"
#include "SelfScrollingWebKitWebView.h"
#include "TextInputController.h"
#include "WebCoreSupport/DumpRenderTreeSupportManx.h"
#include "WebCoreTestSupport.h"
#include "WorkQueue.h"
#include "WorkQueueItem.h"
#endif 
#include <time.h>
#include "common.h"		//current
#include "event.h"		//current
#include "render.h"		//current
#include <string.h>		//sys/sdk/target/include/string.h
#include <libdbg.h>		//sys/sdk/target/include_common/libdbg.h
#include <ctrl.h>		//sys/sdk/target/include/ctrl.h
#include <libdbgfont.h>	//sys/sdk/target/include/libdbgfont.h
#include <stdio.h>		//psp2/dist/include/psp2-compat/stdio.h →/sys/sdk/target/include/stdio.h
#include <net.h>		//sys/sdk/target/include/net.h
#include <libsysmodule.h>//sys/sdk/target/include/libsysmodule.h
//#include <touch.h>	//this file included in the event.h
#include <libperf.h>	//sys/sdk/target/include/libperf.h
#include <stdlib.h>		//sys/sdk/target/include/stdlib.h
//#include <stdio.h>	//double include?


#if defined(USE_WEBKIT)
// WebView interface
#if defined(__SNC__)
#include <webkit/WebView.h>
#else
#include "../builder/psp2-release/include/webkit-1.0/webkit/WebView.h"
#endif
#else
// Dummy interface
#include "WebViewDummy.h"
#endif

extern "C" int setenv(const char *name, const char *value, int overwrite);

// Enable the following define to allow Razor HUD.
//#define ENABLE_RAZOR_HUD

// Enable the following define to create a Razor capture file.
//#define ENABLE_RAZOR_GPU_CAPTURE

#define ENABLE_RAZOE_CPU_CAPTURE

#ifdef ENABLE_RAZOR_GPU_CAPTURE
#include <razor_capture.h>
#endif

// Resource path
#define WEBKIT_FONT_FILE "sa0:/data/font/pvf/jpn0.pvf"
#define WEBKIT_THEME_PATH "host0:/resource/theme"
#define URL_LIST_PATH "app0:/Webkit_RegressionTest_UrlList.txt"

// Device parameters
#define ANALOGSTICK_DEADZONE_LOW    (0x60)
#define ANALOGSTICK_DEADZONE_HIGH    (0xa0)

// control data
bool g_quit = false;
enum {
	DESTROYED,
	RUNNING,
	FINISHED,
	DESTROYING,
} g_test_status;
SceCtrlData g_ctrlData;
TouchEventInfo g_touchEventInfo;
TouchEventInfo g_backtouchEventInfo;

// webview
WebView *g_webView;

// FILE
FILE* fp;
unsigned long g_filesize = 0;
int g_viewcount =0;

// TIME
time_t g_start,g_end;

// Provided for common
int renderDbgFont(void);

// User main thread parameters
extern const char            sceUserMainThreadName[]        = "libgxm_render_main_thr";
extern const int            sceUserMainThreadPriority    = SCE_KERNEL_DEFAULT_PRIORITY_USER;
extern const unsigned int    sceUserMainThreadStackSize    = SCE_KERNEL_STACK_SIZE_DEFAULT_USER_MAIN;

// Libc parameters
unsigned int sceLibcHeapExtendedAlloc = 1;
unsigned int sceLibcHeapSize = SCE_LIBC_HEAP_SIZE_EXTENDED_ALLOC_NO_LIMIT;

// Net
static char s_net_memory[16 * 1024];

// performance analizer
static void init_perf()
{
    // Razorの初期化
    int ret = sceSysmoduleLoadModule( SCE_SYSMODULE_PERF );
    if( ret < 0 )
    {
        printf( "sceSysmoduleLoadModule failed (0x%08x)\n", ret );
    }

    ret = scePerfArmPmonReset( SCE_PERF_ARM_PMON_THREAD_ID_ALL );
    if( ret < 0 )
    {
        printf( "scePerfArmPmonReset() failed (0x%08x)\n", ret );
    }

    char event_code[ SCE_PERF_ARM_PMON_PMCNT_NUM ] =
    {
        SCE_PERF_ARM_PMON_ICACHE_STALL,
        SCE_PERF_ARM_PMON_DCACHE_STALL,
        SCE_PERF_ARM_PMON_BRANCH_MISPREDICT,
        SCE_PERF_ARM_PMON_PREDICT_BRANCH,
        SCE_PERF_ARM_PMON_DATA_READ,
        SCE_PERF_ARM_PMON_DATA_WRITE
    };

    for ( unsigned int i = 0; i < SCE_PERF_ARM_PMON_PMCNT_NUM; ++i)
    {
        ret = scePerfArmPmonSelectEvent( SCE_PERF_ARM_PMON_THREAD_ID_ALL, i, event_code[i] );
        if( ret < 0 )
        {
            printf( "scePerfArmPmonSelectEvent() failed (0x%08x)\n", ret );
        }
    }

    ret = scePerfArmPmonStart( SCE_PERF_ARM_PMON_THREAD_ID_ALL );
    if( ret < 0 )
    {
        printf( "scePerfArmPmonStart() failed (0x%08x)\n", ret );
    }
}

bool fileOpen()
{
    if((fp = fopen(URL_LIST_PATH,"r")) == NULL)
    {
        printf("UrlListFile Open Failed\n");
		return false;
    }else{
        printf("UrllistFile Open Success\n");
        fseek(fp,  0L, SEEK_END);
        g_filesize = ftell(fp);
        fseek(fp, 0L, SEEK_SET);
    }
    return true;

}

char* fileRead()
{
    g_viewcount++;
    static char url_buf[2048];
    memset(url_buf,0x00,sizeof(url_buf));
    if (fgets(url_buf,g_filesize,fp) != NULL){
        printf("\n#%06d url:%s",g_viewcount,url_buf);
        while((strncmp(url_buf,"//",2)==0)){
            memset(url_buf,0x00,sizeof(url_buf));
            if (fgets(url_buf,g_filesize,fp) != NULL){
           	    g_viewcount++;
                printf("#%06d url:%s",g_viewcount,url_buf);
            }
        }
    }
    
    return url_buf;
    
}
void memoryCheck(const char *argv) {
	struct std::malloc_managed_size mmsize;
    malloc_stats(&mmsize);
    printf("#%s#current_system_size \/ max_system_size: %09d \/ %09d\n", argv,mmsize.current_system_size,mmsize.max_system_size);	
    printf("# current_inuse_size \/ max_inuse_size : %09d \/ %09d\n", mmsize.current_inuse_size,mmsize.max_inuse_size);
}

class WebViewStatus {
    char *_title;
    char *_url;
    uint8_t _progress;
public:
    WebViewStatus() :
        _title(new char[1024]),
        _url(new char[1024]),
        _progress(0)
        {}
    ~WebViewStatus() {
        delete [] _title;
        delete [] _url;
    }
    float fps() { return 0.f; }
    float updateTime() { return 0.f; }
    float renderTime() { return 0.f; }
    const char * title() { return _title; }
    void setTitle(const char *title) {
        if (!title) title = "about:blank";
        strncpy(_title, title, 1024);
    }
    const char * url() { return _url; }
    void setUrl(const char *url) {
        if (!url) url = "about:blank";
        strncpy(_url, url, 1024);
    }
    uint8_t progress() { return _progress; }
    void setProgress(uint8_t progress) { _progress = progress; }
};

static WebViewStatus g_webViewStatus;
static bool g_backingStoreUpdated;

static float g_location_x = 0;
static float g_location_y = 0;
static float g_scale = 1.0f;

static bool webViewCallback(WebView *webView, void *userData, CallbackType type, const CallbackData *in, CallbackData *out)
{
    switch (type) {
    case CALLBACK_SET_TEXTURE:
        memcpy(g_testTextureData, in[0].type_uchar_ptr, in[1].type_int);
        return true;
    case CALLBACK_SET_BACKINGSTORE:
        g_backingStoreUpdated = true;
        return true;
    case CALLBACK_SET_TITLE:
        g_webViewStatus.setTitle(in[0].type_char_ptr);
        return true;
    case CALLBACK_SET_URL:
        g_webViewStatus.setUrl(in[0].type_char_ptr);
        return true;
    case CALLBACK_HOVER_LINK_URL:
        return true;
    case CALLBACK_SHOW_POPUP_MENU:
        return true;
    case CALLBACK_HIDE_POPUP_MENU:
        return true;
    case CALLBACK_LOAD_STARTED:
        g_webViewStatus.setProgress(0);
        return true;
    case CALLBACK_LOAD_FINISHED:
        printf("LoadFinished\n");
        g_webViewStatus.setProgress(100);
        g_test_status = FINISHED;
        return true;
    case CALLBACK_LOAD_PROGRESS_CHANGED:
        g_webViewStatus.setProgress((int)(in[0].type_float * 100));
        return true;
    case CALLBACK_LOAD_FAILED:
        printf("Load Failed: '%s' - %s (%d)\n", in[2].type_const_char_ptr, in[3].type_const_char_ptr, in[0].type_int);
		g_end = clock();
		printf("%.2f[sec]\n", (double)(g_end - g_start) / CLOCKS_PER_SEC);
        //g_quit = true;
        return true;
    case CALLBACK_JAVASCRIPT_ALERT:
        printf("JavaScript Alert: %s\n", in[0].type_const_char_ptr);
        return true;
    case CALLBACK_JAVASCRIPT_CONFIRM:
        printf("JavaScript Confirm: %s\n", in[0].type_const_char_ptr);
        printf("Returning 'true'\n");
        out[0].type_bool = true;
        return true;
    case CALLBACK_JAVASCRIPT_PROMPT:
        printf("JavaScript Prompt: %s (default: '%s')\n", in[0].type_const_char_ptr, in[1].type_const_char_ptr);
        printf("Returning default: '%s'\n", in[1].type_const_char_ptr ? in[1].type_const_char_ptr : "null");
        out[0].type_const_char_ptr = in[1].type_const_char_ptr;
        return true;
    case CALLBACK_DESTROY_WEBVIEW:
        g_test_status = DESTROYED;
        return true;
    default:
        return false;
    }
}

void initEvent()
{
    int err = SCE_OK;
    // setup pad events
    err = sceCtrlSetSamplingMode(SCE_CTRL_MODE_DIGITALANALOG);
    SCE_DBG_ASSERT(err == SCE_OK);
    // setup touch events
    err = sceTouchSetSamplingState(SCE_TOUCH_PORT_FRONT, SCE_TOUCH_SAMPLING_STATE_START);
    sceTouchSetSamplingState(SCE_TOUCH_PORT_BACK, SCE_TOUCH_SAMPLING_STATE_START);
    SCE_DBG_ASSERT(err == SCE_OK);
    err = sceTouchEnableTouchForce(SCE_TOUCH_PORT_FRONT);
    SCE_DBG_ASSERT(err == SCE_OK);
    err = touchEventInit(&g_touchEventInfo);
    err = touchEventInit(&g_backtouchEventInfo);
    SCE_DBG_ASSERT(err == SCE_OK);
    err = touchEventSetMoveThreshold(&g_touchEventInfo, MOVE_THRESHOLD);
    err = touchEventSetMoveThreshold(&g_backtouchEventInfo, 5);
    SCE_DBG_ASSERT(err == SCE_OK);
}

void initApp()
{
#if defined(USE_WEBKIT)
    // set env
    setenv("WEBKIT_FONT_FILE", WEBKIT_FONT_FILE, 0);
#if 0
    setenv("WEBKIT_THEME_PATH", WEBKIT_THEME_PATH, 0);
    setenv("WEBKIT_IGNORE_SSL_ERRORS", "", 0);
#endif
#endif
    // create webview instance
	memoryCheck("InitApp\n");
    WebViewManager::init(true, webViewCallback, NULL);
    g_webView = WebViewManager::create(WEBVIEW_WIDTH, WEBVIEW_HEIGHT);
	g_test_status = DESTROYED;
    fileOpen();
}

void updateApp()
{
	switch (g_test_status) {
	case DESTROYED:
		char *url = fileRead();
		if (url[0] == '\0') {
			printf("All URLs have been tested.\n");
			g_end = clock();
			printf("%.2f[sec]\n", (double)(g_end - g_start) / CLOCKS_PER_SEC);
			g_quit = true;
			return;
		}
		g_webView = WebViewManager::create(WEBVIEW_WIDTH, WEBVIEW_HEIGHT);
		memoryCheck("Just before CreatedWebView\n");
		if (!g_webView) {
			printf("WebView creation failed.\n");
			g_quit = true;
			return;
		}
		g_test_status = RUNNING;
		g_webView->loadUrl(url);
		return;
	case RUNNING:
		break;
	case FINISHED:
		if (g_webView) {
			memoryCheck("Just before WebViewDestroyed\n");
			WebViewManager::destroy(g_webView);
			g_test_status = DESTROYING;
		}
		else {
			g_test_status = DESTROYED;
		}
		return;
	case DESTROYING:
		return;
	}
    float x = g_location_x;
    float y = g_location_y;
    float scale = g_scale;
    float delta = 80.0f;
    delta /= scale;
    if (g_ctrlData.lx < ANALOGSTICK_DEADZONE_LOW) {
        x += delta;
    }
    if (g_ctrlData.lx > ANALOGSTICK_DEADZONE_HIGH) {
        x -= delta;
    }
    if (g_ctrlData.ly < ANALOGSTICK_DEADZONE_LOW) {
        y += delta;
    }
    if (g_ctrlData.ly > ANALOGSTICK_DEADZONE_HIGH) {
        y -= delta;
    }
    if (g_ctrlData.ry < ANALOGSTICK_DEADZONE_LOW) {
        scale *= 1.05f;
    }
    if (g_ctrlData.ry > ANALOGSTICK_DEADZONE_HIGH) {
        scale /= 1.05f;
    }

    do {
        // get contents size
        int w, h;
        g_webView->getContentsSize(w, h);
        if (w <= 0 || h <= 0) return;

        // adjust scale
        if (scale > 2.0f) scale = 2.0f;
        if (scale < 1.0f) {
            if (scale * w < WEBVIEW_WIDTH) scale = (float)WEBVIEW_WIDTH / w;
            if (scale * h < WEBVIEW_HEIGHT) scale = (float)WEBVIEW_HEIGHT / h;
        }

        // adjust location
        if (x < 0) x = 0;
        if (x + WEBVIEW_WIDTH / scale > w)
            x = w - WEBVIEW_WIDTH / scale;
        if (y < 0) y = 0;
        if (y + WEBVIEW_HEIGHT / scale > h)
            y = h - WEBVIEW_HEIGHT / scale;
        
        if (g_location_x == x && g_location_y == y && g_scale == scale) {
            // nothing to do
//            break;
        }

        g_location_x = x;
        g_location_y = y;

        g_webView->drawRect((int)g_location_x, (int)g_location_y, (int)(WEBVIEW_WIDTH / scale), (int)(WEBVIEW_HEIGHT / scale), &g_target, sizeof(g_target));
        if (scale == g_scale) {
            g_webView->setClipRect((int)g_location_x, (int)g_location_y, (int)(WEBVIEW_WIDTH / scale), (int)(WEBVIEW_HEIGHT / scale));
        }
        g_scale = scale;
    } while (0);

    // heartbeat
    WebViewManager::update();
}

// Dispatch pad events
static void dispatchPadEvent(SceUInt32 pressed, SceUInt32 released)
{
    if (g_webView) {
        if (pressed & SCE_CTRL_L && g_webView->canGoBack()) {
            g_webView->goBack();
        }
        if (pressed & SCE_CTRL_R && g_webView->canGoForward()) {
            g_webView->goForward();
        }
        if (pressed & SCE_CTRL_UP) {
            g_webView->sendHtmlEvent(HTML_NODE_FOCUS_UP);
        }
        if (pressed & SCE_CTRL_DOWN) {
            g_webView->sendHtmlEvent(HTML_NODE_FOCUS_DOWN);
        }
        if (pressed & SCE_CTRL_LEFT) {
            g_webView->sendHtmlEvent(HTML_NODE_FOCUS_LEFT);
        }
        if (pressed & SCE_CTRL_RIGHT) {
            g_webView->sendHtmlEvent(HTML_NODE_FOCUS_RIGHT);
        }
        if (pressed & SCE_CTRL_CIRCLE) {
            g_webView->sendKeyEvent(KEY_EVENT_PRESSED, KEY_ENTER);
        }
        if (released & SCE_CTRL_CIRCLE) {
            g_webView->sendKeyEvent(KEY_EVENT_RELEASED, KEY_ENTER);
        }
        if (pressed & SCE_CTRL_CROSS) {
            g_webView->stop();
        }
        if (pressed & SCE_CTRL_START) {
            g_webView->reload();
        }
    }
}

// Dispatch touch events
static void dispatchTouchEventSingleTouch(TouchEventElem *elm, MouseEventType type)
{
  //    using namespace sce::Vectormath::Simd::Aos;
    if (g_webView && elm) {
        SceUInt8 touchId = elm->id;
        SceInt16 x = elm->curX >> 1;
        SceInt16 y = elm->curY >> 1;

        if (elm->pNext == NULL) {
            if (convertScreenToLocalCSS(x, y)) {
                g_webView->sendMouseEvent(type, MOUSE_BUTTON_LEFT, x, y);
            }
        }
    }
}

static void dispatchTouchEventBack(TouchEventElem *elm, MouseEventType type)
{
  if (elm) {
    SceInt16 x = elm->curX >> 1;
    SceInt16 y = elm->curY >> 1;
    
    if (type == MOUSE_EVENT_PRESSED) {
      if (convertScreenToLocalCSS(x, y)) {
    popup_mag(1.5f, 1.25f, -0.1f, 2.f, 3.f, (float)x, (float)y, 320.f, 272.f);
      }
    }
    else if (type == MOUSE_EVENT_MOVED) {
      if (elm->pNext == NULL) {
    if (convertScreenToLocalCSS(x, y)) {
      update_mag((float)x, (float)y, 320.f, 272.f);
    }
      }
    }
    else if (type == MOUSE_EVENT_RELEASED) {
      dismiss_mag();
    }
  }
}

// Update all hardware events
static void updateEvent()
{
    // update control buffer
    SceUInt32 prev = g_ctrlData.buttons;
    sceCtrlPeekBufferPositive(0, &g_ctrlData, 1);
    SceUInt32 pressed = g_ctrlData.buttons & ~prev;
    SceUInt32 released = ~g_ctrlData.buttons & prev;
    dispatchPadEvent(pressed, released);

    // update touch info
    SceTouchData data;
    int err = sceTouchRead(SCE_TOUCH_PORT_FRONT, &data, 1);
    SCE_DBG_ASSERT(err >= SCE_OK);
    err = touchEventUpdate(&data, &g_touchEventInfo);

    err = sceTouchRead(SCE_TOUCH_PORT_BACK, &data, 1);
    err = touchEventUpdate(&data, &g_backtouchEventInfo);

    SCE_DBG_ASSERT(err == SCE_OK);
    dispatchTouchEventSingleTouch(g_touchEventInfo.pPress, MOUSE_EVENT_PRESSED);
    dispatchTouchEventSingleTouch(g_touchEventInfo.pMove, MOUSE_EVENT_MOVED);
    dispatchTouchEventSingleTouch(g_touchEventInfo.pRelease, MOUSE_EVENT_RELEASED);

    dispatchTouchEventBack(g_backtouchEventInfo.pPress, MOUSE_EVENT_PRESSED);
    dispatchTouchEventBack(g_backtouchEventInfo.pMove, MOUSE_EVENT_MOVED);
    dispatchTouchEventBack(g_backtouchEventInfo.pRelease, MOUSE_EVENT_RELEASED);

}

// Power Tick Thread to prevent suspend.
static SceInt32 PowerTickLoop(SceSize /*argSize*/, void */*pArgBlock*/)
{
	while (!g_quit) {
		sceKernelPowerTick(SCE_KERNEL_POWER_TICK_DISABLE_AUTO_SUSPEND);
		sceKernelDelayThread(10 * 1000 * 1000 /*us*/);
	}
	return SCE_OK;
}

static void init_power_tick_thread()
{
	SceUID res = sceKernelCreateThread("PowerTick",
									   PowerTickLoop,
									   SCE_KERNEL_DEFAULT_PRIORITY_USER,
									   SCE_KERNEL_STACK_SIZE_DEFAULT_USER_MAIN,
									   0,
									   SCE_KERNEL_CPU_MASK_USER_ALL,
									   NULL);
	if (res < 0) {
		printf("!!!!! PowerTick Thread creation failed %x !!!!\n", res);
		SCE_DBG_ASSERT(res >= 0);
	}
	SceUID thrid = res;
	res = sceKernelStartThread(thrid, 0, NULL);
	if (res != SCE_OK) {
		printf("!!!!! PowerTick Thread cannot start %x !!!!\n", res);
		SCE_DBG_ASSERT(res == SCE_OK);
	}
}



// Entry point
int main(void)
{
    int err = SCE_OK;
    UNUSED(err);

	init_power_tick_thread();

#ifdef ENABLE_RAZOE_CPU_CAPTURE
    init_perf();
#endif

#ifdef ENABLE_RAZOR_HUD
    // Initialize the Razor HUD system.
    // This should be done before the call to sceGxmInitialize().
    err = sceSysmoduleLoadModule( SCE_SYSMODULE_RAZOR_HUD );
    SCE_DBG_ASSERT(err == SCE_OK);
#endif

#ifdef ENABLE_RAZOR_GPU_CAPTURE
    // Initialize the Razor capture system.
    // This should be done before the call to sceGxmInitialize().
    err = sceSysmoduleLoadModule( SCE_SYSMODULE_RAZOR_CAPTURE );
    SCE_DBG_ASSERT(err == SCE_OK);

    // Trigger a capture after 100 frames.
    sceRazorGpuCaptureSetTrigger( 100, "app0:render_to_texture.sgx" );
#endif

    // initialize network library
    err = sceSysmoduleLoadModule( SCE_SYSMODULE_NET );
    SCE_DBG_ASSERT(err == SCE_OK);

    SceNetInitParam param;
    param.memory = s_net_memory;
    param.size = sizeof( s_net_memory );
    param.flags = 0;
    err = sceNetInit( &param );
    SCE_DBG_ASSERT(err == SCE_OK);


    // Load WebKit module
    err = sceKernelLoadStartModule("app0:ScePsp2Compat.suprx", 0, 0, 0, SCE_NULL, NULL);
    SCE_DBG_ASSERT(err > 0);
    err = sceKernelLoadStartModule("app0:SceTileBackend.suprx", 0, 0, 0, SCE_NULL, NULL);
    SCE_DBG_ASSERT(err > 0);
    err = sceKernelLoadStartModule("app0:SceWebKitModule.suprx", 0, 0, 0, SCE_NULL, NULL);
    SCE_DBG_ASSERT(err > 0);

    sceKernelChangeThreadPriority(SCE_KERNEL_THREAD_ID_SELF, SCE_KERNEL_DEFAULT_PRIORITY_USER-10);

    // initialize
    initDbgFont();
    initGxm();

    // create graphics data
    createClearData();
    createTestTextureData(WEBVIEW_WIDTH, WEBVIEW_HEIGHT, WEBVIEW_FORMAT);
    createCubeData();
    createOffscreenBuffer();
    updateRender(); // call once

    initEvent();
    initApp();

    // loop until exit

	//Test Start
	g_start = clock();
	
    while (!g_quit) {
        // do update step
        updateEvent();
        updateApp();

        // render
//        renderOffscreen();
        renderMain();

        // queue a display swap and cycle our buffers
        cycleDisplayBuffers();
    }

    // wait until rendering is done
    sceGxmFinish(g_context);

    // destroy graphics data
    destroyOffscreenBuffer();
    destroyCubeData();
    destroyTestTextureData();
    destroyClearData();

    // terminate graphics
    termGxm();
    termDbgFont();

    // terminate network library
    sceNetTerm();
    sceSysmoduleUnloadModule( SCE_SYSMODULE_NET );
    
#ifdef ENABLE_RAZOR_GPU_CAPTURE
    // Terminate Razor capture.
    // This should be done after the call to sceGxmTerminate().
    sceSysmoduleUnloadModule( SCE_SYSMODULE_RAZOR_CAPTURE );
#endif

#ifdef ENABLE_RAZOR_HUD
    // Terminate Razor HUD.
    // This should be done after the call to sceGxmTerminate().
    sceSysmoduleUnloadModule( SCE_SYSMODULE_RAZOR_HUD );
#endif
    fclose(fp);

    return SCE_OK;
}

static SceInt32 g_text_x = 0, g_text_y = 0;
#define TEXT_LINE_HEIGHT 16

void debugPrintf(const char *s)
{
    if (s) {
#if !defined(DISABLE_DEBUG_FONT)
        sceDbgFontPrint(g_text_x, g_text_y, 0xffffffff, (const SceChar8 *)s);
#endif
        g_text_y += TEXT_LINE_HEIGHT;
    } else {
        g_text_y += (TEXT_LINE_HEIGHT / 2);
    }
}

void touchTrace(TouchEventElem *elm)
{
    while (elm) {
//        TouchEvent touchEvent = elm->event;
        SceUInt8 touchId = elm->id;
        SceInt16 touchEventX = elm->curX;
        SceInt16 touchEventY = elm->curY;
//        SceInt16 touchEventF = elm->curF;
//        SceInt16 touchDeltaX = elm->deltaX;
//        SceInt16 touchDeltaY = elm->deltaY;
//        SceInt16 touchDeltaF = elm->deltaF;
//        SceUInt64 touchDeltaTime = elm->deltaT;
//        SceUInt64 touchElapsedTime = elm->elapsedT;
        char str[32];
//        snprintf(str, sizeof(str), "Touch event (%d) x:%04d y:%04d f:%04d dx:%04d dy:%04d df:%04d time:%ld elapsedTime:%ld\n", touchId, touchEventX, touchEventY, touchEventF, touchDeltaX, touchDeltaY, touchDeltaF, touchDeltaTime, touchElapsedTime);
        snprintf(str, sizeof(str), "Touch event (%d) x:%04d y:%04d\n", touchId, touchEventX, touchEventY);
        debugPrintf(str);
        elm = elm->pNext;
    }
}

int renderDbgFont(void)
{
    // status
    g_text_x = 20;
    g_text_y = 20;
    debugPrintf("WebKit Test");
    debugPrintf(0);
#if 0
    {
        char str[32];
        snprintf(str, sizeof(str), "FPS: %.2f", g_webViewStatus.fps());
        debugPrintf(str);
    }
    {
        char str[32];
        snprintf(str, sizeof(str), "Update Time: %.3f ms", g_webViewStatus.updateTime());
        debugPrintf(str);
    }
    {
        char str[32];
        snprintf(str, sizeof(str), "Render Time: %.3f ms", g_webViewStatus.renderTime());
        debugPrintf(str);
    }
#endif
    {
        debugPrintf("Title");
        char str[64];
        snprintf(str, sizeof(str), "%s", g_webViewStatus.title());
        debugPrintf(str);
    }
    debugPrintf(0);
    {
        debugPrintf("URL");
        char str[64];
        snprintf(str, sizeof(str), "%s", g_webViewStatus.url());
        debugPrintf(str);
    }
    debugPrintf(0);
    {
        debugPrintf("Loading");
        char str[32];
        snprintf(str, sizeof(str), "%d%%", g_webViewStatus.progress());
        debugPrintf(str);
    }
    debugPrintf(0);
#if 0
    {
        bool buttonUp       = g_ctrlData.buttons & SCE_CTRL_UP;
        bool buttonDown     = g_ctrlData.buttons & SCE_CTRL_DOWN;
        bool buttonLeft     = g_ctrlData.buttons & SCE_CTRL_LEFT;
        bool buttonRight    = g_ctrlData.buttons & SCE_CTRL_RIGHT;
        bool buttonStart    = g_ctrlData.buttons & SCE_CTRL_START;
        bool buttonSelect   = g_ctrlData.buttons & SCE_CTRL_SELECT;
        bool buttonCircle   = g_ctrlData.buttons & SCE_CTRL_CIRCLE;
        bool buttonCross    = g_ctrlData.buttons & SCE_CTRL_CROSS;
        bool buttonSquare   = g_ctrlData.buttons & SCE_CTRL_SQUARE;
        bool buttonTriangle = g_ctrlData.buttons & SCE_CTRL_TRIANGLE;
        bool buttonL        = g_ctrlData.buttons & SCE_CTRL_L;
        bool buttonR        = g_ctrlData.buttons & SCE_CTRL_R;
        char str[32];
        snprintf(str, sizeof(str), "KeyState: %d%d%d%d %d%d%d%d %d%d%d%d", buttonUp, buttonDown, buttonLeft, buttonRight, buttonTriangle, buttonCross, buttonSquare, buttonCircle, buttonL, buttonR, buttonSelect, buttonStart);
        debugPrintf(str);
    }
    debugPrintf(0);
    {
        touchTrace(g_touchEventInfo.pPress);
        touchTrace(g_touchEventInfo.pHold);
        touchTrace(g_touchEventInfo.pMove);
        touchTrace(g_touchEventInfo.pAbort);
        touchTrace(g_touchEventInfo.pRelease);
    }
#endif

    // usage
    g_text_x = 760;
    g_text_y = 350;
    debugPrintf("Pad Controls");
    debugPrintf(0);
    debugPrintf("Arrow   : Focus navi");
    debugPrintf("CIRCLE  : Decide");
    debugPrintf("CROSS   : Stop loading");
    debugPrintf("START   : Reload");
    debugPrintf("L       : Go back");
    debugPrintf("R       : Go forward");
    debugPrintf("L-Stick : Scroll");

    return SCE_OK;
}

extern "C" void *nullptr = 0;
	